﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using IQReportes.Utils;
using System.Data.SqlClient;
using System.Data;
using IQReportes.WebService;
using System.Globalization;

namespace IQReportes.Models.Logica
{
    public class LogicaCargaCSV
    {
        private Dictionary<string, DIR> diccionarioDIR = new Dictionary<string, DIR>();
        private Dictionary<string, int> diccionarioTipos = new Dictionary<string, int>();
        private Dictionary<string, int> diccionarioEstados = new Dictionary<string, int>();
        private DateTime fechaBase;
        
        private EstadoTransaccionDAO dbEstadoTxn = new EstadoTransaccionDAO();
        private TipoTransaccionDAO dbTiposTxn = new TipoTransaccionDAO();
        private DirDAO dbDIR = new DirDAO();
        private FechaCSVDAO dbFecha = new FechaCSVDAO();
        private ConexionSQL dbSQL = new ConexionSQL();
        private ConsolidadoCSVDAO dbConsolidadoCSV = new ConsolidadoCSVDAO();

        private ConsumeRestService restService = new ConsumeRestService();
        public bool existeFechaCSV(string stFecha, int codInstitucion)
        {
            try
            {
                var fechas = dbFecha.fechas.Where(a => a.vcFecha == stFecha && a.inCodInstitucion == codInstitucion);
                if (fechas.Count() > 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch(Exception e)
            {
                string gg = e.Message;
                return true;
            }
        }

        public bool guardarNuevaFechaCSV(DateTime dtfecha, int codInstitucion,FechaCSV fecha)
        {
            try
            {
                String stFecha = String.Format("{0:dd/MM/yyyy}", dtfecha);
                fecha.vcFecha = stFecha;
                fecha.inCodInstitucion = codInstitucion;
                dbFecha.fechas.Add(fecha);
                dbFecha.SaveChanges();
                return true;
                
            }
            catch (Exception e)
            {
                string gg = e.Message;
                return false;
            }
        }

        private void cargarDatosIniciales(int institucion)
        {
            try
            {
                //falta filtrar lis tipos y estados segun institucion
                var listaTiposTxn = dbTiposTxn.tiposTransaccion;
                var listaEstadosTxn = dbEstadoTxn.estados;
                var listaDIR = dbDIR.dirs;
                
                foreach (var t in listaTiposTxn)
                {
                    diccionarioTipos.Add(t.vcNombre, t.idTipo);
                }

                foreach (var e in listaEstadosTxn)
                {
                    diccionarioEstados.Add(e.vcNombre, e.idEstado);
                }

                foreach (var d in listaDIR)
                {
                    diccionarioDIR.Add(d.vcCodDIR, d);
                }
                fechaBase = DateTime.Now.AddDays(-1);
            }
            catch (Exception e)
            {
                string gg = e.Message;
            }
        }

        public ErrorCSV cargarTransaccionesCsv(string path, string nombreArch, int confirmacion,
                                                DateTime fechaCSV, int institucion)
        {
            List<ConsolidadoTransaccion> listaConsolidado = new List<ConsolidadoTransaccion>();
            List<DIR> listaDIRs = new List<DIR>();
            ConsolidadoTransaccion consActual;
            List<string> auxDirsConError = new List<string>();
            double tipoCambio;
            FechaCSV fechaObj = new FechaCSV();
            ErrorCSV errorCSV = new ErrorCSV();
            errorCSV.dirErroneos = new List<String>();
            try
            {
                cargarDatosIniciales(institucion);
                path = path + "\\" + nombreArch;
                string[] csvData = File.ReadAllLines(path);
                var transacciones = (from i in csvData
                                     let rowData = i.Split(',') //splits each row into an array called rowData
                                     select new Transaccion
                                     {
                                         vcHora = rowData[0],
                                         vcNemonico = rowData[1],
                                         vcDIR = rowData[2],
                                         vcEstado = rowData[3],
                                         vcNombreTransaccion = rowData[4],
                                         dbMonto = Double.Parse(rowData[11]),
                                         inMoneda = Int32.Parse(rowData[12])
                                     }).ToList();

                if (confirmacion == Constantes.CONFIRMACION_OK)
                {
                    tipoCambio = llamarWSTipoCambio(fechaCSV, fechaObj);
                    foreach (Transaccion t in transacciones)
                    {
                        if (t.inMoneda == Constantes.MONEDA_DOLARES)
                        {
                            t.dbMonto = t.dbMonto * tipoCambio;
                        }
                    }
                }

                var gg = transacciones.AsQueryable().GroupBy(a =>
                    new
                    {
                        a.vcNombreTransaccion,
                        a.vcEstado,
                        a.vcDIR
                    }).Select(a => new ConsolidadoTransaccion
                    {
                        tipoTransaccion = a.Key.vcNombreTransaccion,
                        estadoTransaccion = a.Key.vcEstado,
                        codDIR = a.Key.vcDIR,
                        cantidadTransacciones = a.Count(),
                        monto = a.Sum(x => x.dbMonto)
                    });
                if (gg == null)
                {
                    errorCSV.sinError = false;
                    return errorCSV;
                }
                foreach (var item in gg)
                {
                    if (diccionarioDIR.ContainsKey(item.codDIR))
                    {
                        consActual = item;
                        listaDIRs.Add(diccionarioDIR[item.codDIR]); //ESTO VA SERVIR PARA EL PROCESO BATCH CUANDO SE ACTUALICE
                        //LOS DIRS MEDIANTE WEB SERVICES
                        if (diccionarioTipos.ContainsKey(item.tipoTransaccion))
                        {
                            item.idTipoTransaccion = diccionarioTipos[item.tipoTransaccion];
                        }
                        if (diccionarioEstados.ContainsKey(item.estadoTransaccion))
                        {
                            item.idEstadoTransaccion = diccionarioEstados[item.estadoTransaccion];
                        }
                        if (item.idTipoTransaccion != null && item.idTipoTransaccion != 0
                            && item.idEstadoTransaccion != null && item.idEstadoTransaccion != 0)
                        {   
                            listaConsolidado.Add(item);
                        }
                    }
                    else
                    {
                        auxDirsConError.Add(item.codDIR);
                    }

                }

                var dirsActualizados = listaDIRs.AsQueryable().Distinct();
                var listaDirsError = auxDirsConError.Distinct();
                foreach (var e in listaDirsError)
                {
                    errorCSV.dirErroneos.Add(e);
                }

                if (confirmacion == Constantes.CONFIRMACION_OK)
                {
                    DateTime antes, despues;
                    antes = DateTime.Now;
                    //DateTime fecha = DateTime.Now.AddDays(-4);
                    cargarDatosTransacciones(listaConsolidado, fechaCSV, institucion);

                    var flag = guardarNuevaFechaCSV(fechaCSV, institucion,fechaObj);
                    despues = DateTime.Now;
                    TimeSpan intervalo;
                    intervalo = despues.Subtract(antes);
                    int kk = intervalo.Minutes;
                    foreach (DIR d in dirsActualizados)
                    {
                        actualizarUltimaTxnDIR(d, fechaCSV);
                    }   
                }

                errorCSV.sinError = true;
                return errorCSV;
            }
            catch (Exception e)
            {
                string ff = e.Message;
                errorCSV.sinError = false;
                return errorCSV;
            }
        }

        
        private double llamarWSTipoCambio(DateTime fechaCSV, FechaCSV fecObj)
        {
            double tipoCambio;
            tipoCambio = restService.getCurrency(fechaCSV);
            string stFecha;

            //return tipoCambio;

            if (tipoCambio <= 0)
            {
                //coger de BD el tipo d cambio de una fecha más cercana
                
                stFecha = fechaCSV.ToString("dd/MM/yyyy");
                DataSet ds = new DataSet();
                List<SqlParameter> parms = new List<SqlParameter>();
                SqlParameter par1 = new SqlParameter("@fecha", SqlDbType.VarChar, 50);
                par1.Value = stFecha;
                parms.Add(par1);
                dbSQL.ejecutarSelectProcedureDS(ProcedimientosAlmacenados.obtiene_tipo_cambio_cercano,
                    parms, ds);

                string stFecAux;
                string stTipoCambio = Constantes.CADENA_VACIA;
                
                foreach (DataRow dr in ds.Tables[0].Rows)
                {
                    stFecAux = dr[0].ToString();
                    stTipoCambio = dr[1].ToString();
                    
                    if (Constantes.CADENA_VACIA.Equals(stTipoCambio))
                    {
                        tipoCambio = 2.70;
                    }
                    else
                    {
                        tipoCambio = Double.Parse(stTipoCambio);
                    }
                    if (!Constantes.CADENA_VACIA.Equals(stFecAux))
                    {
                        stFecha = DateTime.ParseExact(stFecAux, "dd/MM/yyyy", CultureInfo.InvariantCulture).ToString("dd/MM/yyyy");
                    }
                    fecObj.vcFechaAux = stFecha;
                    fecObj.inFuncionoWS = 0;
                }
                
            }
            else
            {
                fecObj.inFuncionoWS = 1;
            }

            fecObj.dbTipoCambio = tipoCambio;

            return tipoCambio;
            //guardar el tipo de cambio en BD
        }


        private bool cargarDatosTransacciones(List<ConsolidadoTransaccion> listaConsolidado, DateTime fecha, int institucion)
        {
            try
            {
                List<ConsolidadoCSV> listaTempConsCSV;
                List<ConsolidadoCSV> listaConsCSV = new List<ConsolidadoCSV>();
                int antiguedadMes = calcularAntiguedadxPeriodo(Constantes.TBL_CONS_MES, fecha);
                int antiguedadSemana = calcularAntiguedadxPeriodo(Constantes.TBL_CONS_SEMANA, fecha); ;
                int antiguedadDia = calcularAntiguedadxPeriodo(Constantes.TBL_CONS_DIA, fecha);
                int ano = fecha.Year;
                foreach (ConsolidadoTransaccion c in listaConsolidado)
                {
                    listaTempConsCSV = new List<ConsolidadoCSV>();
                    llenaListaTemporalConsolidadoCSV(c.codDIR, c.idEstadoTransaccion, c.idTipoTransaccion, c.cantidadTransacciones,
                                                    c.monto, fecha, institucion, listaTempConsCSV,antiguedadMes,antiguedadSemana,
                                                    antiguedadDia,ano);
                    foreach (ConsolidadoCSV cons in listaTempConsCSV)
                    {
                        listaConsCSV.Add(cons);
                    }
                }

                List<string[]> listaData = new List<string[]>();
                for (int k = 0; k < listaConsCSV.Count(); k++)
                {
                    listaData.Add(listaConsCSV[k].obtenerArreglo());

                    if (k % 3000 == 0)
                    {
                        grabarConsolidadoSQL(listaData, Constantes.NUM_COLS_CONSCSV, listaData.Count(), Constantes.TBL_CONSCSV);
                        listaData = new List<string[]>();
                    }
                }
                if (listaData != null && listaData.Count() > 0)
                {
                    grabarConsolidadoSQL(listaData, Constantes.NUM_COLS_CONSCSV, listaData.Count(), Constantes.TBL_CONSCSV);
                }
                //ejecutar stored de carga
                actualizaConsolidados();
                //ejecutar stored que borre data con flag de actualizado en 1
                eliminaConsolidadosActualizados();
                return true;
            }
            catch(Exception e)
            {
                return false;
            }
        }

        private void actualizaConsolidados()
        {
            string nombreStored = ProcedimientosAlmacenados.actualiza_consolidado_csv;
            List<SqlParameter> listaParametros = new List<SqlParameter>();
            try
            {
                dbSQL.ejecturarNoReaderProcedure(listaParametros, nombreStored);
            }
            catch (Exception e)
            {
                throw;
            }
        }

        private void eliminaConsolidadosActualizados()
        {
            string nombreStored = ProcedimientosAlmacenados.elimina_cons_actualizados_csv;
            List<SqlParameter> listaParametros = new List<SqlParameter>();
            try
            {
                dbSQL.ejecturarNoReaderProcedure(listaParametros, nombreStored);
            }
            catch (Exception e)
            {
                throw;
            }
        }

        private void llenaListaTemporalConsolidadoCSV(string dir, int idEstado, int idTipoTxn, int cantTxn, double monto,
                                                        DateTime fecha, int institucion, List<ConsolidadoCSV> listConsCSV,
                                                        int antiguedadMes, int antiguedadSemana, int antiguedadDia, int ano)
        {
            //calcular antiguedadPeriodo
            ConsolidadoCSV consCSV;
            
            try
            {
                consCSV = new ConsolidadoCSV();
                construirElementoConsolidadoCSV(dir, idEstado, idTipoTxn, cantTxn, monto, Constantes.CODIGO_ANO, ano, institucion,consCSV);
                listConsCSV.Add(consCSV);

                if (antiguedadMes != -1)
                {
                    consCSV = new ConsolidadoCSV();
                    construirElementoConsolidadoCSV(dir, idEstado, idTipoTxn, cantTxn, monto, Constantes.CODIGO_MES, antiguedadMes, institucion, consCSV);
                    listConsCSV.Add(consCSV);
                }

                if (antiguedadSemana != -1)
                {
                    consCSV = new ConsolidadoCSV();
                    construirElementoConsolidadoCSV(dir, idEstado, idTipoTxn, cantTxn, monto, Constantes.CODIGO_SEMANA, antiguedadSemana, institucion, consCSV);
                    listConsCSV.Add(consCSV);
                }

                if (antiguedadDia != -1)
                {
                    consCSV = new ConsolidadoCSV();
                    construirElementoConsolidadoCSV(dir, idEstado, idTipoTxn, cantTxn, monto, Constantes.CODIGO_DIA, antiguedadDia, institucion, consCSV);
                    listConsCSV.Add(consCSV);
                }
            }
            catch (Exception e)
            {
                throw;
            }
        }

        private void construirElementoConsolidadoCSV(string codDir, int idEstado, int idTipoTxn, int cantTxn,
                                            double monto, int codigoTabla, int antiguedadPeriodo, int institucion,
                                            ConsolidadoCSV consCSV)
        {
            //string nombreStored = "";
            //List<SqlParameter> listaParametros = new List<SqlParameter>();
            
            consCSV.idEstado = idEstado;
            consCSV.idTipo = idTipoTxn;
            consCSV.codDir = codDir;
            consCSV.cantTxn = cantTxn;
            consCSV.monto = monto;
            consCSV.codTabla = codigoTabla;
            consCSV.antiguedadPeriodo = antiguedadPeriodo;
            consCSV.institucion = institucion;
            consCSV.actualizado = 0;
            consCSV.fechaRegistro = DateTime.Now;
            //switch (codigoTabla)
            //{
            //    case 1:
            //        nombreStored = ProcedimientosAlmacenados.actualiza_consolidado_dia;
            //        break;
            //    case 2:
            //        nombreStored = ProcedimientosAlmacenados.actualiza_consolidado_semana;
            //        break;
            //    case 3:
            //        nombreStored = ProcedimientosAlmacenados.actualiza_consolidado_mes;
            //        break;
            //    case 4:
            //        nombreStored = ProcedimientosAlmacenados.actualiza_consolidado_ano;
            //        break;
            //}

            //SqlParameter param1 = new SqlParameter("@idEstado", SqlDbType.Int);
            //SqlParameter param2 = new SqlParameter("@idTipo", SqlDbType.Int);
            //SqlParameter param3 = new SqlParameter("@codDir", SqlDbType.VarChar, 30);
            //SqlParameter param4 = new SqlParameter("@cantTxn", SqlDbType.Int);
            //SqlParameter param5 = new SqlParameter("@monto", SqlDbType.Float);
            //SqlParameter param6 = new SqlParameter("@codTabla", SqlDbType.Int); //al final no se utiliza este parámetro
            //SqlParameter param7 = new SqlParameter("@antiguedadPeriodo", SqlDbType.Int);
            //SqlParameter param8 = new SqlParameter("@institucion", SqlDbType.Int);

            
            //param1.Value = idEstado;
            //param2.Value = idTipoTxn;
            //param3.Value = codDir;
            //param4.Value = cantTxn;
            //param5.Value = monto;
            //param6.Value = codigoTabla;
            //param7.Value = antiguedadPeriodo;
            //param8.Value = institucion;

            //listaParametros.Add(param1);
            //listaParametros.Add(param2);
            //listaParametros.Add(param3);
            //listaParametros.Add(param4);
            //listaParametros.Add(param5);
            //listaParametros.Add(param6);
            //listaParametros.Add(param7);
            //listaParametros.Add(param8);
            //try
            //{
            //    dbSQL.ejecturarNoReaderProcedure(listaParametros, nombreStored);
            //}
            //catch (Exception e)
            //{
            //    throw;
            //}

            
        }

        private void actualizarUltimaTxnDIR(DIR dir, DateTime fechaCSV)
        {
            DIR dirActualizado = new DIR();
            try
            {
                dirActualizado = diccionarioDIR[dir.vcCodDIR];
                if (diccionarioDIR[dir.vcCodDIR].dtFechaUltimaTxn == null)
                {
                    dirActualizado.dtFechaUltimaTxn = fechaCSV;
                    dbDIR.Entry(dirActualizado).State = EntityState.Modified;
                    dbDIR.SaveChanges();
                }
                else if (diccionarioDIR[dir.vcCodDIR].dtFechaUltimaTxn < fechaCSV)
                {
                    dirActualizado.dtFechaUltimaTxn = fechaCSV;
                    dbDIR.Entry(dirActualizado).State = EntityState.Modified;
                    dbDIR.SaveChanges();
                }
                else
                {
                    //caso contrrio no se hace nada
                }
            }
            catch (Exception e)
            {
                string gg = e.Message;
            }
        }

        private int calcularAntiguedadxPeriodo(string nombreTabla, DateTime fechaRecibida)
        {
            int antiguedad = -1;
            TimeSpan intervalo;
            fechaBase = (new LogicaParametro()).getFechaFinalTx();
            intervalo = fechaBase.Subtract(fechaRecibida);
            //SE ASUME QUE LA FECHA RECIBIDA SIEMPRE VA A SER MENOR QUE LA FECHA BASE
            if (Constantes.TBL_CONS_MES.Equals(nombreTabla))
            {
                if (fechaRecibida.Year == fechaBase.Year)
                {
                    antiguedad = fechaBase.Month - fechaRecibida.Month;
                }
                else
                {
                    antiguedad = 12 - fechaRecibida.Month + fechaBase.Month;
                }
                if (antiguedad > 11) { antiguedad = -1; }
            }
            if (Constantes.TBL_CONS_SEMANA.Equals(nombreTabla))
            {
                var gg = DayOfWeek.Saturday - fechaBase.DayOfWeek;
                fechaBase = fechaBase.AddDays(gg);
                intervalo = fechaBase.Subtract(fechaRecibida);
                antiguedad = intervalo.Days / 7;
                if (antiguedad > 7) { antiguedad = 6; }
            }
            if (Constantes.TBL_CONS_DIA.Equals(nombreTabla))
            {
                antiguedad = intervalo.Days;
                if (antiguedad > 6) { antiguedad = 6; }
            }
            return antiguedad;
        }


        //****************************METODOS QUE ALIMENTAN A GRABARCONSOLIDADO SQL*************************************


        private void grabarConsolidadoSQL(List<string[]> consolidado, int numCols, int numReg, string nombreTabla)
        {
            DataTable dt = new DataTable();

            for (int i = 0; i < numCols; i++)
            {
                dt.Columns.Add(new DataColumn());
            }

            foreach (string[] data in consolidado)
            {
                DataRow row = dt.NewRow();
                row.ItemArray = data;
                dt.Rows.Add(row);
            }

            colocarNombreColumnaDT(dt, nombreTabla);

            using (SqlConnection cn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["conexionPrincipalIQ"].ConnectionString))
            {
                cn.Open();
                using (SqlBulkCopy copy = new SqlBulkCopy(cn))
                {
                    mapearDataTable(copy, nombreTabla);
                    copy.BatchSize = numReg;
                    copy.DestinationTableName = nombreTabla;
                    copy.WriteToServer(dt);
                }
                cn.Close();
            }
        }

        private void colocarNombreColumnaDT(DataTable dt, string nombreTabla)
        {
            int j = 0;
            dt.Columns[j].ColumnName = "idEstado"; j++;
            dt.Columns[j].ColumnName = "idTipo"; j++;
            dt.Columns[j].ColumnName = "codDir"; j++;
            dt.Columns[j].ColumnName = "cantTxn"; j++;
            dt.Columns[j].ColumnName = "monto"; j++;
            dt.Columns[j].ColumnName = "codTabla"; j++;
            dt.Columns[j].ColumnName = "antiguedadPeriodo"; j++;
            dt.Columns[j].ColumnName = "institucion"; j++;
            dt.Columns[j].ColumnName = "actualizado"; j++;
            dt.Columns[j].ColumnName = "fechaRegistro"; j++;
                        
        }
        private void mapearDataTable(SqlBulkCopy copy, string nombreTabla)
        {
            copy.ColumnMappings.Add("idEstado", "idEstado");
            copy.ColumnMappings.Add("idTipo", "idTipo");
            copy.ColumnMappings.Add("codDir", "codDir");
            copy.ColumnMappings.Add("cantTxn", "cantTxn");
            copy.ColumnMappings.Add("monto", "monto");
            copy.ColumnMappings.Add("codTabla", "codTabla");
            copy.ColumnMappings.Add("antiguedadPeriodo", "antiguedadPeriodo");
            copy.ColumnMappings.Add("institucion", "institucion");
            copy.ColumnMappings.Add("actualizado", "actualizado");
            copy.ColumnMappings.Add("fechaRegistro", "fechaRegistro");
        }

    }
}